package com.pokermind.dse.tcp.client;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.pokermind.dse.tcp.protocol.ProtocolConstants;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * TCP Test Client TCP测试客户端，用于测试PokerMind TCP服务器
 *
 * 功能特性： 1. 支持长连接管理 2. 异步消息发送和接收 3. 协议消息编解码 4. 请求响应匹配 5. 连接状态管理
 *
 * @author PokerMind Team
 */
public class TcpTestClient {

    private static final Logger logger = LoggerFactory.getLogger(TcpTestClient.class);

    private final String host;
    private final int port;
    private final ObjectMapper objectMapper = new ObjectMapper();

    // Netty相关组件
    private EventLoopGroup group;
    private Channel channel;
    private Bootstrap bootstrap;

    // 请求响应管理
    private final AtomicLong requestIdGenerator = new AtomicLong(1);
    private final ConcurrentHashMap<Long, CompletableFuture<String>> pendingRequests = new ConcurrentHashMap<>();

    // 连接状态
    private volatile boolean connected = false;

    public TcpTestClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    /**
     * 连接到TCP服务器
     */
    public CompletableFuture<Void> connect() {
        if (connected) {
            return CompletableFuture.completedFuture(null);
        }

        group = new NioEventLoopGroup();
        bootstrap = new Bootstrap();

        bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
            .option(ChannelOption.SO_KEEPALIVE, true).handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast("handler", new TcpClientHandler());
                }
            });

        CompletableFuture<Void> connectFuture = new CompletableFuture<>();

        bootstrap.connect(host, port).addListener((ChannelFutureListener)future -> {
            if (future.isSuccess()) {
                channel = future.channel();
                connected = true;
                logger.info("Connected to TCP server: {}:{}", host, port);
                connectFuture.complete(null);
            } else {
                logger.error("Failed to connect to TCP server: {}:{}", host, port, future.cause());
                connectFuture.completeExceptionally(future.cause());
            }
        });

        return connectFuture;
    }

    /**
     * 断开连接
     */
    public CompletableFuture<Void> disconnect() {
        if (!connected) {
            return CompletableFuture.completedFuture(null);
        }

        CompletableFuture<Void> closeFuture = new CompletableFuture<>();

        if (channel != null) {
            channel.close().addListener((ChannelFutureListener)future -> {
                connected = false;
                if (group != null) {
                    group.shutdownGracefully().addListener(groupFuture -> {
                        logger.info("Disconnected from TCP server");
                        closeFuture.complete(null);
                    });
                } else {
                    closeFuture.complete(null);
                }
            });
        } else {
            closeFuture.complete(null);
        }

        return closeFuture;
    }

    /**
     * 发送协议消息并等待响应
     */
    public CompletableFuture<String> sendMessage(short protocolId, Object requestData) {
        return sendMessage(protocolId, requestData, 10, TimeUnit.SECONDS);
    }

    /**
     * 发送协议消息并等待响应（带超时）
     */
    public CompletableFuture<String> sendMessage(short protocolId, Object requestData, long timeout,
        TimeUnit timeUnit) {
        if (!connected || channel == null || !channel.isActive()) {
            return CompletableFuture.failedFuture(new IllegalStateException("Client not connected"));
        }

        try {
            // 序列化请求数据
            String jsonRequest = objectMapper.writeValueAsString(requestData);
            byte[] jsonBytes = jsonRequest.getBytes(CharsetUtil.UTF_8);

            // 生成请求ID
            long requestId = requestIdGenerator.getAndIncrement();

            // 创建响应Future
            CompletableFuture<String> responseFuture = new CompletableFuture<>();
            pendingRequests.put(requestId, responseFuture);

            // 设置超时
            responseFuture.orTimeout(timeUnit.toMillis(timeout), TimeUnit.MILLISECONDS)
                .whenComplete((result, throwable) -> {
                    pendingRequests.remove(requestId);
                    if (throwable != null) {
                        logger.warn("Request {} timed out or failed: {}", requestId, throwable.getMessage());
                    }
                });

            // 构建协议消息
            ByteBuf buffer = Unpooled.buffer();
            buffer.writeShort(protocolId);           // 协议ID (2字节)
            buffer.writeInt(jsonBytes.length);       // 消息长度 (4字节)
            buffer.writeBytes(jsonBytes);            // JSON数据 (N字节)

            // 发送消息
            channel.writeAndFlush(buffer);

            if (logger.isDebugEnabled()) {
                logger.debug("Sent message: protocolId={}, requestId={}, data={}", protocolId, requestId, jsonRequest);
            }

            return responseFuture;

        } catch (Exception e) {
            logger.error("Error sending message: {}", e.getMessage(), e);
            return CompletableFuture.failedFuture(e);
        }
    }

    /**
     * 检查连接状态
     */
    public boolean isConnected() {
        return connected && channel != null && channel.isActive();
    }

    /**
     * TCP客户端处理器
     */
    private class TcpClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

        private ByteBuf buffer = Unpooled.buffer();

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
            // 累积接收到的数据
            buffer.writeBytes(msg);

            // 尝试解析完整的协议消息
            while (buffer.readableBytes() >= 6) { // 至少需要协议头(6字节)
                buffer.markReaderIndex();

                short protocolId = buffer.readShort();
                int bodyLength = buffer.readInt();

                // 检查消息体是否完整
                if (buffer.readableBytes() < bodyLength) {
                    buffer.resetReaderIndex(); // 回滚，等待更多数据
                    break;
                }

                // 读取消息体
                byte[] jsonBytes = new byte[bodyLength];
                buffer.readBytes(jsonBytes);
                String jsonResponse = new String(jsonBytes, CharsetUtil.UTF_8);

                if (logger.isDebugEnabled()) {
                    logger.debug("Received response: protocolId={}, body={}", protocolId, jsonResponse);
                }

                // 处理响应（这里简化处理，实际可以根据协议ID匹配请求）
                handleResponse(protocolId, jsonResponse);
            }
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            logger.info("Connection to server closed");
            connected = false;

            // 取消所有待处理的请求
            pendingRequests.values()
                .forEach(future -> future.completeExceptionally(new RuntimeException("Connection closed")));
            pendingRequests.clear();

            super.channelInactive(ctx);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            logger.error("Client handler error: {}", cause.getMessage(), cause);
            ctx.close();
        }

        /**
         * 处理服务器响应
         */
        private void handleResponse(short protocolId, String jsonResponse) {
            // 简化实现：完成最早的待处理请求
            // 实际实现中应该根据请求ID匹配
            if (!pendingRequests.isEmpty()) {
                Long firstRequestId = pendingRequests.keys().nextElement();
                CompletableFuture<String> future = pendingRequests.remove(firstRequestId);
                if (future != null && !future.isDone()) {
                    future.complete(jsonResponse);
                }
            } else {
                logger.warn("Received unexpected response: protocolId={}, body={}", protocolId, jsonResponse);
            }
        }
    }

    // ===============================================
    // 便利方法 - 各种协议消息发送
    // ===============================================

    /**
     * 发送心跳消息
     */
    public CompletableFuture<String> sendHeartbeat() {
        return sendMessage(ProtocolConstants.HEARTBEAT_REQ,
            java.util.Map.of("timestamp", System.currentTimeMillis(), "client_id", "test-client"));
    }

    /**
     * 发送认证消息
     */
    public CompletableFuture<String> sendAuth(String token, String userId) {
        return sendMessage(ProtocolConstants.AUTH_REQ,
            java.util.Map.of("token", token, "user_id", userId, "client_version", "1.0.0"));
    }

    /**
     * 发送服务器状态查询
     */
    public CompletableFuture<String> sendServerStatus() {
        return sendMessage(ProtocolConstants.SERVER_STATUS_REQ, java.util.Map.of("client_id", "test-client"));
    }

    /**
     * 发送创建手牌消息
     */
    public CompletableFuture<String> sendCreateHand(Object createHandRequest) {
        return sendMessage(ProtocolConstants.CREATE_HAND_REQ, createHandRequest);
    }

    /**
     * 发送更新Hero底牌消息
     */
    public CompletableFuture<String> sendUpdateHeroCards(Long handRecordId, java.util.List<String> holeCards) {
        return sendMessage(ProtocolConstants.UPDATE_HERO_CARDS_REQ,
            java.util.Map.of("hand_record_id", handRecordId, "hole_cards", holeCards));
    }

    /**
     * 发送街道转换消息
     */
    public CompletableFuture<String> sendTransitionStreet(Object transitionStreetRequest) {
        return sendMessage(ProtocolConstants.TRANSITION_STREET_REQ, transitionStreetRequest);
    }

    /**
     * 发送记录行动消息
     */
    public CompletableFuture<String> sendRecordAction(Object actionRequest) {
        return sendMessage(ProtocolConstants.RECORD_ACTION_REQ, actionRequest);
    }

    /**
     * 发送完成手牌消息
     */
    public CompletableFuture<String> sendCompleteHand(Object completeHandRequest) {
        return sendMessage(ProtocolConstants.COMPLETE_HAND_REQ, completeHandRequest);
    }

    /**
     * 发送AI分析消息
     */
    public CompletableFuture<String> sendAIAnalysis(Object aiAnalysisRequest) {
        return sendMessage(ProtocolConstants.AI_ANALYZE_REQ, aiAnalysisRequest);
    }
}
